home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / dev / devScsiDevice.c < prev    next >
C/C++ Source or Header  |  1991-08-11  |  16KB  |  532 lines

  1. /* 
  2.  * devScsiDevice.c --
  3.  *
  4.  *    Routines for attaching/releasing/sending commands to SCSI device 
  5.  *    attached to SCSI HBAs.
  6.  *
  7.  * Copyright 1989 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /sprite/src/kernel/dev/RCS/devScsiDevice.c,v 9.7 91/07/17 11:46:47 jhh Exp $ SPRITE (Berkeley)";
  19. #endif /* not lint */
  20.  
  21. #include "sprite.h"
  22. #include "scsiDevice.h"
  23. #include "scsiHBA.h"
  24. #include "dev/scsi.h"
  25. #include "scsi.h"
  26. #include "devQueue.h"
  27. #include "fs.h"
  28. #include "sync.h"    
  29. #include "stdlib.h"
  30. #include "bstring.h"
  31.  
  32.  
  33.  
  34. /*
  35.  *----------------------------------------------------------------------
  36.  *
  37.  * DevScsiAttachDevice --
  38.  *
  39.  *    Return a handle that allows access to the specified SCSI device.
  40.  *
  41.  * Results:
  42.  *    A pointer to the ScsiDevice structure for the device. 
  43.  *    NIL if the device could not be attached.
  44.  *
  45.  * Side effects:
  46.  *    If the device is attached, an INQUIRY command is sent to the 
  47.  *    device.
  48.  *
  49.  *----------------------------------------------------------------------
  50.  */
  51.  
  52. ScsiDevice   *
  53. DevScsiAttachDevice(devicePtr, insertProc)
  54.     Fs_Device    *devicePtr;    /* Device to attach. */
  55.     void     (*insertProc)(); /* Insert procedure to use. */
  56. {
  57.     int     hbaType;    
  58.     ScsiDevice   *handle;
  59.     ScsiCmd    inquiryCmdBlock;
  60.  
  61.     /*
  62.      * Call the Attach procedure for the HBA type specified in the Fs_Device.
  63.      */
  64.     hbaType = SCSI_HBA_TYPE(devicePtr);
  65.  
  66.     if (hbaType >= devScsiNumHBATypes) {
  67.     handle = (ScsiDevice *) NIL;
  68.     } else {
  69.     handle = (devScsiAttachProcs[hbaType])(devicePtr,insertProc);
  70.     }
  71.     /*
  72.      * If the inquiry data doesn't exists for this device yet send the
  73.      * device a INQUIRY command. We try it twice because the device might
  74.      * abort the INQUIRY with a UNIT_ATTENTION.
  75.      */
  76.     if ((handle != (ScsiDevice *) NIL) && (handle->inquiryLength == 0)) {
  77.     unsigned char statusByte;
  78.     int    tryNumber = 1;
  79.     ReturnStatus    status = SUCCESS;
  80.  
  81.     while((tryNumber <= 2) && (handle->inquiryLength == 0) && 
  82.           (status != DEV_TIMEOUT)) { 
  83.         if (handle->inquiryDataPtr == (char *) 0) {
  84.         handle->inquiryDataPtr = (char *) malloc(DEV_MAX_INQUIRY_SIZE);
  85.         }
  86.         (void) DevScsiGroup0Cmd(handle,SCSI_INQUIRY,0,DEV_MAX_INQUIRY_SIZE,
  87.                     &inquiryCmdBlock);
  88.         inquiryCmdBlock.buffer = handle->inquiryDataPtr;
  89.         inquiryCmdBlock.bufferLen = DEV_MAX_INQUIRY_SIZE;
  90.         inquiryCmdBlock.dataToDevice = FALSE;
  91.         status =  DevScsiSendCmdSync(handle, &inquiryCmdBlock, &statusByte, 
  92.             &(handle->inquiryLength),  (int *) NIL, (char *) NIL);
  93.         if (status != SUCCESS) {
  94.         handle->inquiryLength = 0;
  95.         }
  96.         tryNumber++;
  97.     }
  98.     }
  99.     if (handle != (ScsiDevice *) NIL) {
  100.     handle->referenceCount++;
  101.     }
  102.     return handle;
  103. }
  104.  
  105. /*
  106.  * The following structure and routine are used to implement DevScsiSendCmdSync.
  107.  * The arguments to DevScsiSendCmdSync are stored in a SyncCmdBuf on 
  108.  * the caller's stack and DevScsiSendCmd is called. The call back function
  109.  * scsiDoneProc fills in the OUT arguments are wakes the caller.
  110.  *
  111.  */
  112. typedef struct SyncCmdBuf {
  113.     Sync_Semaphore mutex;      /* Lock for synronizing updates of 
  114.                    * this structure with the call back 
  115.                    * function. */
  116.     Sync_Condition wait;      /* Condition valued used to wait for
  117.                    * callback. */
  118.     Boolean      done;          /* Is the operation finished or not? */
  119.     unsigned char *statusBytePtr; /* Area to store SCSI status byte. */
  120.     int          *senseBufferLenPtr; /* Sense buffer length pointer. */
  121.     Address    senseBufferPtr;      /* Sense buffer. */
  122.     ReturnStatus status;         /* HBA error for command. */
  123.     int           *countPtr;      /* Btes transferred pointer. */
  124. } SyncCmdBuf;
  125.  
  126. /*
  127.  *----------------------------------------------------------------------
  128.  *
  129.  * scsiDoneProc --
  130.  *
  131.  *    This procedure is called when a scsi command started by 
  132.  *    DevScsiSendCmdSync finished. It's calling sequence is 
  133.  *    defined by the call back done by the DevScsiSendCmd routine.
  134.  *
  135.  * Results:
  136.  *    None
  137.  *
  138.  * Side effects:
  139.  *    A scsi command is executed.
  140.  *
  141.  *----------------------------------------------------------------------
  142.  */
  143.  
  144. static int
  145. scsiDoneProc(scsiCmdPtr, errorCode, statusByte, byteCount, 
  146.          senseDataLen, senseDataPtr)
  147.     ScsiCmd    *scsiCmdPtr;
  148.     ReturnStatus errorCode;
  149.     unsigned char statusByte;
  150.     int        byteCount;
  151.     int        senseDataLen;
  152.     Address    senseDataPtr;
  153. {
  154.     SyncCmdBuf    *syncCmdDataPtr = (SyncCmdBuf *) (scsiCmdPtr->clientData);
  155.     /*
  156.      * A pointer to a SyncCmdBuf is passed as the clientData to this call.
  157.      * Lock the structure, fill in the return values and wake up the
  158.      * initiator.
  159.      */
  160.     MASTER_LOCK(&syncCmdDataPtr->mutex);
  161.  
  162.     *(syncCmdDataPtr->statusBytePtr) = statusByte;
  163.     syncCmdDataPtr->status = errorCode;
  164.     *(syncCmdDataPtr->countPtr) = byteCount;
  165.     if (syncCmdDataPtr->senseBufferLenPtr != (int *) NIL) {
  166.     int    len;
  167.     len = *(syncCmdDataPtr->senseBufferLenPtr);
  168.     if (senseDataLen < len) {
  169.         len = senseDataLen;
  170.     }
  171.     bcopy(senseDataPtr, syncCmdDataPtr->senseBufferPtr, len);            *(syncCmdDataPtr->senseBufferLenPtr) = len;
  172.     }
  173.     syncCmdDataPtr->done = TRUE;
  174.     Sync_MasterBroadcast(&syncCmdDataPtr->wait);
  175.     MASTER_UNLOCK(&syncCmdDataPtr->mutex);
  176.     return (0);
  177.  
  178. }
  179.  
  180. /*
  181.  *----------------------------------------------------------------------
  182.  *
  183.  * DevScsiSendCmdSync --
  184.  *
  185.  *    Send a SCSI command block to the device specified in the 
  186.  *    ScsiDevice. This is the synchronous version that waits
  187.  *    for the status byte and sense data before returning.
  188.  *
  189.  * Results:
  190.  *    A ReturnStatus
  191.  *
  192.  * Side effects: 
  193.  *    A SCSI command block is sent to the device.  
  194.  *
  195.  *----------------------------------------------------------------------
  196.  *
  197.  */
  198. ReturnStatus
  199. DevScsiSendCmdSync(scsiDevicePtr, scsiCmdPtr, statusBytePtr, 
  200.             amountTransferredPtr,  senseBufferLenPtr, senseBufferPtr)
  201.     ScsiDevice *scsiDevicePtr;  /* Handle for target device. */
  202.     ScsiCmd    *scsiCmdPtr;            /* SCSI command to be sent. */
  203.     unsigned char *statusBytePtr; /* Area to store SCSI status byte. */
  204.     int        *amountTransferredPtr; /* OUT - Nuber of bytes transferred. */
  205.     Address     senseBufferPtr;      /* Buffer to put sense data upon error. */
  206.     int        *senseBufferLenPtr; /*  IN - Length of senseBuffer available.
  207.                      * OUT - Length of senseData returned. */
  208. {
  209.     ReturnStatus status;
  210.     SyncCmdBuf     syncCmdData;
  211.  
  212.     scsiCmdPtr->clientData = (ClientData) &syncCmdData;
  213.     scsiCmdPtr->doneProc = scsiDoneProc;
  214.     Sync_SemInitDynamic((&syncCmdData.mutex),"ScsiSyncCmdMutex");
  215.     syncCmdData.done = FALSE;
  216.     syncCmdData.statusBytePtr = statusBytePtr;
  217.     syncCmdData.senseBufferPtr = senseBufferPtr;
  218.     syncCmdData.senseBufferLenPtr = senseBufferLenPtr;
  219.     syncCmdData.countPtr = amountTransferredPtr;
  220.     DevScsiSendCmd(scsiDevicePtr, scsiCmdPtr);
  221.     MASTER_LOCK((&syncCmdData.mutex));
  222.     while (syncCmdData.done == FALSE) { 
  223.     Sync_MasterWait((&syncCmdData.wait),(&syncCmdData.mutex),FALSE);
  224.     }
  225.     MASTER_UNLOCK((&syncCmdData.mutex));
  226.     status = syncCmdData.status;
  227.     Sync_SemClear(&syncCmdData.mutex);
  228.     return status;
  229. }
  230.  
  231.  
  232.  
  233. /*
  234.  *----------------------------------------------------------------------
  235.  *
  236.  * DevScsiSendCmd --
  237.  *
  238.  *    Send a SCSI command block to the device specified in the 
  239.  *    ScsiDevice.
  240.  *
  241.  * Results:
  242.  *    Nothing
  243.  *
  244.  * Side effects: 
  245.  *    A SCSI command block enqueue for the device.  The doneProc procedure
  246.  *    is call upon SCSI command completion.
  247.  *
  248.  *----------------------------------------------------------------------
  249.  *
  250.  * Due to the simplity of this routine and as an attempt to reduce procedure
  251.  * calling depth, this routine is coded as a macro and can be found in
  252.  * scsiHBAInt.h. 
  253.  */
  254. #ifndef DevScsiSendCmd
  255. void 
  256. DevScsiSendCmd(scsiDevicePtr, scsiCmdPtr)
  257.     ScsiDevice       *scsiDevicePtr;      /* Handle for target device. */
  258.     ScsiCmd         *scsiCmdPtr;        /* Command to be executed. */
  259. {
  260.      Dev_QueueInsert(scsiDevicePtr->devQueue, (List_Links *) scsiCmdPtr);
  261. }
  262. #endif /* DevScsiSendCmd */
  263.  
  264.  
  265. /*
  266.  *----------------------------------------------------------------------
  267.  *
  268.  * DevScsiReleaseDevice --
  269.  *
  270.  *     Release a device previously attached with ScsiAttachDevice().
  271.  *
  272.  * Results:
  273.  *    A ReturnStatus.
  274.  *
  275.  * Side effects: 
  276.  *    Unknown.
  277.  *
  278.  *----------------------------------------------------------------------
  279.  */
  280. ReturnStatus 
  281. DevScsiReleaseDevice(scsiDevicePtr)
  282.     ScsiDevice *scsiDevicePtr;  /* Handle for device to be released. */
  283. {
  284.     scsiDevicePtr->referenceCount--;
  285.     if (scsiDevicePtr->referenceCount == 0) { 
  286.     free(scsiDevicePtr->inquiryDataPtr);
  287.     scsiDevicePtr->inquiryDataPtr = (char *) 0;
  288.     scsiDevicePtr->inquiryLength = 0;
  289.     }
  290.     return ((scsiDevicePtr->releaseProc)(scsiDevicePtr));
  291. }
  292.  
  293.  
  294. /*
  295.  *----------------------------------------------------------------------
  296.  *
  297.  *  DevScsiGetSenseCmd --
  298.  *
  299.  *     Procedure for formatting REQUEST SENSE.
  300.  *
  301.  * Results:
  302.  *    void
  303.  *
  304.  * Side effects: 
  305.  *    Unknown.
  306.  *
  307.  *----------------------------------------------------------------------
  308.  */
  309. void
  310. DevScsiSenseCmd(scsiDevicePtr, bufferSize, buffer, scsiCmdPtr)
  311.     ScsiDevice *scsiDevicePtr;  /* Handle for device to be released. */
  312.     int        bufferSize;    /* Size of request sense data buffer. */
  313.     char    *buffer;    /* Data buffer to put sense data. */
  314.     ScsiCmd    *scsiCmdPtr;    /* Scsi command buffer to fill in. */
  315. {
  316.     DevScsiGroup0Cmd(scsiDevicePtr, SCSI_REQUEST_SENSE, 0, 
  317.             (unsigned) bufferSize, scsiCmdPtr);
  318.     scsiCmdPtr->dataToDevice = FALSE;
  319.     scsiCmdPtr->bufferLen = bufferSize;
  320.     scsiCmdPtr->buffer = buffer;
  321. }
  322.  
  323. /*
  324.  *----------------------------------------------------------------------
  325.  *
  326.  *  DevScsiTestReady --
  327.  *
  328.  *     Test to see if a SCSI device is ready.
  329.  *
  330.  * Results:
  331.  *    
  332.  *
  333.  * Side effects: 
  334.  *    A TEST_UNIT_READY command is set to the device.
  335.  *
  336.  *----------------------------------------------------------------------
  337.  */
  338. ReturnStatus
  339. DevScsiTestReady(scsiDevicePtr)
  340.     ScsiDevice *scsiDevicePtr;  /* Handle for device to be released. */
  341. {
  342.     ScsiCmd    unitReadyCmd;    /* Scsi command buffer to fill in. */
  343.     ReturnStatus status;
  344.     unsigned char    statusByte;
  345.     int        len;
  346.     char    senseData[SCSI_MAX_SENSE_LEN];
  347.     int        senseLen;
  348.     char    errorString[MAX_SCSI_ERROR_STRING];
  349.     Boolean    class7;
  350.  
  351.     DevScsiGroup0Cmd(scsiDevicePtr,SCSI_TEST_UNIT_READY, 0, 0, &unitReadyCmd);
  352.     unitReadyCmd.bufferLen = 0;
  353.     len = 0;
  354.     senseLen = SCSI_MAX_SENSE_LEN;
  355.     status = DevScsiSendCmdSync(scsiDevicePtr,&unitReadyCmd, &statusByte,
  356.                 &len, &senseLen, senseData);
  357.     class7 = DevScsiMapClass7Sense(senseLen, senseData, &status, errorString);
  358.     if (class7) {
  359.     return status;
  360.     }
  361.     return status;
  362. }
  363. /*
  364.  *----------------------------------------------------------------------
  365.  *
  366.  *  DevScsiStartStopUnit --
  367.  *
  368.  *     Test to see if a SCSI device is ready.
  369.  *
  370.  * Results:
  371.  *    
  372.  *
  373.  * Side effects: 
  374.  *    A TEST_UNIT_READY command is set to the device.
  375.  *
  376.  *----------------------------------------------------------------------
  377.  */
  378. ReturnStatus
  379. DevScsiStartStopUnit(scsiDevicePtr, start)
  380.     ScsiDevice *scsiDevicePtr;  /* Handle for device to be released. */
  381.     Boolean start;
  382. {
  383.     ScsiCmd        scsiCmd;    /* Scsi command buffer to fill in. */
  384.     ScsiStartStopCmd     *cmdPtr;
  385.     ReturnStatus     status;
  386.     unsigned char    statusByte;
  387.     int            len;
  388.  
  389.     bzero((char *) &scsiCmd, sizeof(ScsiCmd));
  390.     scsiCmd.commandBlockLen = sizeof(ScsiStartStopCmd);
  391.     scsiCmd.bufferLen = 0;
  392.     cmdPtr = (ScsiStartStopCmd *) scsiCmd.commandBlock;
  393.     cmdPtr->command = SCSI_START_STOP;
  394.     cmdPtr->unitNumber = scsiDevicePtr->LUN;
  395.     cmdPtr->immed = 0;
  396.     cmdPtr->loadEject = 0;
  397.     cmdPtr->start = (start == TRUE) ? 1 : 0;
  398.     len = 0;
  399.     status = DevScsiSendCmdSync(scsiDevicePtr,&scsiCmd, &statusByte,
  400.                 &len, (int *) NIL, (Address) NIL);
  401.     return status;
  402. }
  403.  
  404.  
  405. /*
  406.  *----------------------------------------------------------------------
  407.  *
  408.  * DevScsiIOControl --
  409.  *
  410.  *    Process a generic SCSI device IOControl.
  411.  *
  412.  * Results:
  413.  *    The return status of the IOControl.
  414.  *
  415.  * Side effects:
  416.  *    Many.
  417.  *
  418.  *----------------------------------------------------------------------
  419.  */
  420. /*ARGSUSED*/
  421. ReturnStatus
  422. DevScsiIOControl(devPtr, ioctlPtr, replyPtr)
  423.     ScsiDevice    *devPtr;    /* SCSI Handle for device. */
  424.     Fs_IOCParam *ioctlPtr;    /* Standard I/O Control parameter block */
  425.     Fs_IOReply *replyPtr;    /* Size of outBuffer and returned signal */
  426. {
  427.     ReturnStatus    status;
  428.     int            senseDataLen;
  429.     char        senseData[DEV_MAX_SENSE_BYTES];
  430.  
  431.     if (ioctlPtr->command == IOC_SCSI_COMMAND) {
  432.     Dev_ScsiCommand     *cmdPtr;
  433.     Dev_ScsiStatus     *statusPtr;
  434.     ScsiCmd         scsiCmd;
  435.     unsigned char    statusByte;
  436.     Boolean        dataToDevice;
  437.     int        senseBufLen;
  438.     /*
  439.      * The user wants to send a SCSI command. First validate
  440.      * the input buffer is large enough to have the parameter
  441.      * block and the output buffer can hold the return status.
  442.      */
  443.     if ((ioctlPtr->inBufSize < sizeof(Dev_ScsiCommand)) ||
  444.         (ioctlPtr->outBufSize < sizeof(Dev_ScsiStatus))) {
  445.         return(GEN_INVALID_ARG);
  446.     }
  447.     cmdPtr = (Dev_ScsiCommand *) ioctlPtr->inBuffer;
  448.     /*
  449.      * Validate the SCSI command block.
  450.      */
  451.     if ((cmdPtr->commandLen < 0) || (cmdPtr->commandLen > 16) ||
  452.         (cmdPtr->commandLen > 
  453.         (ioctlPtr->inBufSize-sizeof(Dev_ScsiCommand))))    {
  454.         return(GEN_INVALID_ARG);
  455.     }
  456.     /*
  457.      * Validate the input or output data buffer.
  458.      */
  459.     dataToDevice = (cmdPtr->dataOffset < ioctlPtr->inBufSize);
  460.     if ((cmdPtr->bufferLen < 0) ||
  461.         (cmdPtr->bufferLen > devPtr->maxTransferSize)) {
  462.         return FS_BUFFER_TOO_BIG;
  463.     }
  464.     if (dataToDevice && 
  465.         (cmdPtr->bufferLen > (ioctlPtr->inBufSize - cmdPtr->dataOffset))) {
  466.         return (GEN_INVALID_ARG);
  467.     }
  468.     if (!dataToDevice && 
  469.        (cmdPtr->bufferLen > (ioctlPtr->outBufSize-sizeof(Dev_ScsiStatus)))){
  470.         return (GEN_INVALID_ARG);
  471.     }
  472.     /*
  473.      * Things look ok. Fill in the ScsiCmd for the device.
  474.      */
  475.     scsiCmd.dataToDevice = dataToDevice;
  476.     scsiCmd.bufferLen = cmdPtr->bufferLen;
  477.     scsiCmd.buffer = dataToDevice ? 
  478.                 (ioctlPtr->inBuffer+cmdPtr->dataOffset) :
  479.                 (ioctlPtr->outBuffer + sizeof(Dev_ScsiStatus));
  480.  
  481.     scsiCmd.commandBlockLen = cmdPtr->commandLen;
  482.     bcopy(ioctlPtr->inBuffer+sizeof(Dev_ScsiCommand), scsiCmd.commandBlock,
  483.           scsiCmd.commandBlockLen);
  484.     statusPtr = (Dev_ScsiStatus *) ioctlPtr->outBuffer;
  485.     senseDataLen = DEV_MAX_SENSE_BYTES;
  486.     status = DevScsiSendCmdSync(devPtr, &scsiCmd, &statusByte,
  487.         &(statusPtr->amountTransferred), &senseDataLen, senseData);
  488.     statusPtr->statusByte = statusByte;
  489.         statusPtr->senseDataLen = senseDataLen;
  490.     senseBufLen = (ioctlPtr->outBufSize - sizeof(Dev_ScsiStatus) - 
  491.             statusPtr->amountTransferred);
  492.         if (senseBufLen > senseDataLen) {
  493.         senseBufLen = senseDataLen;
  494.     }
  495.     if (senseBufLen >= 0) {
  496.         bcopy(senseData, 
  497.             (char *)(ioctlPtr->outBuffer + sizeof(Dev_ScsiStatus)
  498.                      + statusPtr->amountTransferred) ,
  499.                      senseBufLen);
  500.     }
  501.     return status;
  502.     } else {
  503.     return GEN_INVALID_ARG;
  504.     }
  505.  
  506. }
  507.  
  508. /*
  509.  *----------------------------------------------------------------------
  510.  *
  511.  * DevNoHBAAttachDevice --
  512.  *
  513.  *    A SCSI HBA attach procedure that always returns no device. This
  514.  *    routine should be inserted into attach procedure for HBA types
  515.  *    that aren't support on the machine.
  516.  *
  517.  * Results:
  518.  *    NIL 
  519.  *
  520.  * Side effects:
  521.  *
  522.  *----------------------------------------------------------------------
  523.  */
  524. /*ARGSUSED*/
  525. ScsiDevice *
  526. DevNoHBAAttachDevice(devicePtr, insertProc)
  527.     Fs_Device    *devicePtr;    /* Device to attach. */
  528.     void     (*insertProc)(); /* Insert procedure to use. */
  529. {
  530.     return (ScsiDevice *) NIL;
  531. }
  532.